import sys
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import os

class PatchEditorGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Git Patch 编辑工具 ✂️")
        self.root.geometry("1000x800")  # 宽度 1000，高度 800
        self.root.resizable(True, True)

        # 数据存储
        self.metadata = ""  # 全局元信息（含所有头部和结尾标志）
        self.files = []     # 文件及修改块数据
        self.hunk_vars = [] # 修改块勾选状态

        # 创建界面
        self._create_widgets()

    def _create_widgets(self):
        # ===== 顶部：文件选择区 =====
        file_frame = ttk.LabelFrame(self.root, text="📂 文件选择", padding=10)
        file_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(file_frame, text="输入 Patch 文件:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.input_path = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.input_path, width=60).grid(row=0, column=1, padx=5, pady=5)  # 增加输入框宽度
        ttk.Button(file_frame, text="浏览...", command=self._browse_input).grid(row=0, column=2, padx=5, pady=5)

        ttk.Label(file_frame, text="输出 Patch 文件:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.output_path = tk.StringVar(value="new_patch.patch")
        ttk.Entry(file_frame, textvariable=self.output_path, width=60).grid(row=1, column=1, padx=5, pady=5)
        ttk.Button(file_frame, text="浏览...", command=self._browse_output).grid(row=1, column=2, padx=5, pady=5)

        ttk.Button(file_frame, text="🔍 加载 Patch", command=self._parse_and_display).grid(row=0, column=3, rowspan=2, padx=10, pady=5)

        # ===== 中部：修改块选择区 =====
        hunk_frame = ttk.LabelFrame(self.root, text="✏️ 修改块选择 (勾选保留项)", padding=10)
        hunk_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # 全选/取消全选按钮（固定在滚动区外）
        select_all_outer_frame = ttk.Frame(hunk_frame)
        select_all_outer_frame.pack(fill=tk.X, pady=(0, 10))
        ttk.Button(select_all_outer_frame, text="✅ 全选", command=self._select_all).pack(side="left", padx=5)
        ttk.Button(select_all_outer_frame, text="❌ 取消全选", command=self._deselect_all).pack(side="left", padx=5)

        # 滚动区域
        self.hunk_canvas = tk.Canvas(hunk_frame)
        self.hunk_scrollbar = ttk.Scrollbar(hunk_frame, orient="vertical", command=self.hunk_canvas.yview)
        self.hunk_inner_frame = ttk.Frame(self.hunk_canvas)

        self.hunk_inner_frame.bind("<Configure>", lambda e: self.hunk_canvas.configure(scrollregion=self.hunk_canvas.bbox("all")))
        self.hunk_canvas.create_window((0, 0), window=self.hunk_inner_frame, anchor="nw")
        self.hunk_canvas.configure(yscrollcommand=self.hunk_scrollbar.set)

        self.hunk_canvas.pack(side="left", fill=tk.BOTH, expand=True)
        self.hunk_scrollbar.pack(side="right", fill=tk.Y)

        # ===== 底部：操作按钮区 =====
        btn_frame = ttk.Frame(self.root, padding=10)
        btn_frame.pack(fill=tk.X, padx=10, pady=5, side=tk.BOTTOM)
        ttk.Button(btn_frame, text="🎉 生成新 Patch", command=self._generate_patch).pack(side="right", padx=5)
        ttk.Button(btn_frame, text="❌ 清空", command=self._clear_all).pack(side="right", padx=5)

    def _browse_input(self):
        path = filedialog.askopenfilename(title="选择输入 Patch 文件", filetypes=[("Patch 文件", "*.patch"), ("所有文件", "*.*")])
        if path:
            self.input_path.set(path)

    def _browse_output(self):
        path = filedialog.asksaveasfilename(title="保存新 Patch 文件", defaultextension=".patch", filetypes=[("Patch 文件", "*.patch"), ("所有文件", "*.*")])
        if path:
            self.output_path.set(path)

    def _parse_and_display(self):
        input_path = self.input_path.get()
        if not input_path or not os.path.exists(input_path):
            messagebox.showerror("错误", "请选择有效的输入文件！")
            return

        self._clear_hunk_frame()
        self.metadata, self.files = self._parse_patch_file(input_path)

        if not self.files:
            messagebox.showerror("解析失败", "无法解析 Patch 文件，请检查格式！")
            return

        total_hunks = sum(len(f['hunks']) for f in self.files)
        self.hunk_vars = []

        for file_idx, file_data in enumerate(self.files):
            file_label = ttk.Label(
                self.hunk_inner_frame, 
                text=f"📄 文件: {file_data['header'].split('+++ b/')[-1].strip()}",
                font=("SimHei", 10, "bold"),
                foreground="#2c3e50"
            )
            file_label.pack(fill=tk.X, padx=5, pady=(10, 5))

            for hunk_idx, hunk in enumerate(file_data['hunks']):
                var = tk.BooleanVar(value=True)
                self.hunk_vars.append((file_idx, hunk_idx, var))

                hunk_container = ttk.Frame(self.hunk_inner_frame, borderwidth=1, relief="solid", padding=5)
                hunk_container.pack(fill=tk.X, padx=5, pady=5)

                ttk.Checkbutton(
                    hunk_container, 
                    text=f"修改块 #{hunk_idx+1}", 
                    variable=var
                ).pack(anchor="w", padx=5, pady=2)

                hunk_text = scrolledtext.ScrolledText(
                    hunk_container, 
                    height=8, 
                    width=100,          # 关键：增加宽度至 100
                    font=("SimHei", 9),
                    wrap=tk.WORD
                )
                hunk_text.insert(tk.END, ''.join(hunk['lines']))
                hunk_text.configure(state="disabled")
                hunk_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=2)

        messagebox.showinfo("成功", f"已加载 {len(self.files)} 个文件，共 {total_hunks} 个修改块")

    def _generate_patch(self):
        input_path = self.input_path.get()
        output_path = self.output_path.get()

        if not input_path or not output_path:
            messagebox.showerror("错误", "请选择输入和输出文件路径！")
            return

        if not self.files:
            messagebox.showerror("错误", "请先加载 Patch 文件！")
            return

        file_hunks = {}
        for file_idx, hunk_idx, var in self.hunk_vars:
            if var.get():
                if file_idx not in file_hunks:
                    file_hunks[file_idx] = []
                file_hunks[file_idx].append(hunk_idx)

        if not file_hunks:
            messagebox.showwarning("警告", "未选择任何修改块！")
            return

        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write(self.metadata)
                for file_idx in sorted(file_hunks.keys()):
                    file_data = self.files[file_idx]
                    f.write(file_data['header'])
                    for hunk_idx in file_hunks[file_idx]:
                        f.write(''.join(file_data['hunks'][hunk_idx]['lines']))

            messagebox.showinfo("成功", f"🎉 新 Patch 已生成：\n{output_path}")
            os.startfile(os.path.dirname(output_path))
        except Exception as e:
            messagebox.showerror("生成失败", f"错误：{str(e)}")

    def _clear_all(self):
        self.input_path.set("")
        self.output_path.set("new_patch.patch")
        self._clear_hunk_frame()
        self.files = []
        self.hunk_vars = []
        self.metadata = ""

    def _clear_hunk_frame(self):
        for widget in self.hunk_inner_frame.winfo_children():
            widget.destroy()

    def _select_all(self):
        for _, _, var in self.hunk_vars:
            var.set(True)
        messagebox.showinfo("提示", "✅ 已全选所有修改块")

    def _deselect_all(self):
        for _, _, var in self.hunk_vars:
            var.set(False)
        messagebox.showinfo("提示", "❌ 已取消所有选中项")

    @staticmethod
    def _parse_patch_file(file_path):
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
        except Exception as e:
            messagebox.showerror("读取失败", f"无法读取文件：{str(e)}")
            return "", None

        metadata = []
        files = []
        current_file = None
        current_hunk = None
        in_file_header = False

        for line in lines:
            if current_file is None:
                if line.startswith("diff --git"):
                    current_file = {'header': line, 'hunks': []}
                    current_hunk = None
                    in_file_header = True
                else:
                    metadata.append(line)
            elif in_file_header:
                if line.startswith("@@ "):
                    current_hunk = {'lines': [line]}
                    in_file_header = False
                else:
                    current_file['header'] += line
            elif current_hunk is not None:
                if line.startswith("diff --git"):
                    current_file['hunks'].append(current_hunk)
                    files.append(current_file)
                    current_file = {'header': line, 'hunks': []}
                    current_hunk = None
                    in_file_header = True
                elif line.startswith("@@ "):
                    current_file['hunks'].append(current_hunk)
                    current_hunk = {'lines': [line]}
                else:
                    current_hunk['lines'].append(line)

        if current_file is not None:
            if current_hunk is not None:
                current_file['hunks'].append(current_hunk)
            files.append(current_file)

        return ''.join(metadata), files

if __name__ == "__main__":
    root = tk.Tk()
    root.option_add("*Font", "SimHei 10")
    app = PatchEditorGUI(root)
    root.mainloop()